iT邦幫忙

0

Day 9|讓 /add 不只格式正確,日期時間也要真的合理

  • 分享至 

  • xImage
  •  

一、今天的學習目標

前幾天我已經讓 /add 可以接收三個欄位:

title

date

time

而且也有使用 Regex 檢查日期是不是 8 位數、時間是不是 4 位數。

const dateRegex = /^\d{8}$/;
const timeRegex = /^\d{4}$/;

這樣可以擋掉像:

2026-10-05
18:30

這種不符合目前輸入規則的格式。

但今天遇到一個新的問題。

就算格式正確,也不代表內容真的合理。

例如:

20260230
2960

這兩組資料分別都是 8 位數和 4 位數,但實際上:

2 月 30 日不存在
29:60 也不是合法時間

所以今天的目標,就是在原本的格式驗證後面,再增加一層「實際日期時間驗證」。

整體流程變成:

使用者輸入資料
↓
檢查格式
↓
拆出年月日時分
↓
檢查日期是否存在
↓
檢查時間是否合理
↓
檢查是不是過去的時間
↓
全部通過才繼續

二、今天新增的程式邏輯

原本的 add.js 已經會取得使用者輸入的資料:

const title = interaction.options.getString('title');
const date = interaction.options.getString('date');
const time = interaction.options.getString('time');

接著先維持原本的 Regex 驗證:

const dateRegex = /^\d{8}$/;
const timeRegex = /^\d{4}$/;

const errors = [];

if (!dateRegex.test(date)) {
    errors.push('日期格式錯誤,請輸入 8 位數日期,例如 20260903');
}

if (!timeRegex.test(time)) {
    errors.push('時間格式錯誤,請輸入 4 位數時間,例如 1830');
}

if (errors.length > 0) {
    await interaction.reply(errors.join('\n'));
    return;
}

這一層只負責確認:

日期是不是 8 位數
時間是不是 4 位數

格式通過之後,我再把資料拆開:

const year = parseInt(date.slice(0, 4));
const month = parseInt(date.slice(4, 6));
const day = parseInt(date.slice(6, 8));

const hour = parseInt(time.slice(0, 2));
const minute = parseInt(time.slice(2, 4));

例如:

date = 20261005

會被拆成:

year = 2026
month = 10
day = 5

而:

time = 1830

則會被拆成:

hour = 18
minute = 30

這裡我也使用 parseInt(),把原本的字串轉成數字,因為後面要進行數值比較。

接著先檢查時間範圍:

if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
    errors.push('時間不存在,請輸入正確的時間,例如 1830');
}

因為 24 小時制中:

hour 必須介於 0~23
minute 必須介於 0~59

所以像:

2960

就會被擋下來。

接著建立 JavaScript 的 Date:

const eventDate = new Date(
    year,
    month - 1,
    day,
    hour,
    minute
);

這裡有一個要注意的地方:

JavaScript 的 Date 月份是從 0 開始計算。

也就是:

0 = 1 月
1 = 2 月
...
11 = 12 月

所以輸入的月份要寫成:

month - 1

接著檢查日期是否真的存在:

const isValidDate =
    eventDate.getFullYear() === year &&
    eventDate.getMonth() === month - 1 &&
    eventDate.getDate() === day;

if (!isValidDate) {
    errors.push('日期不存在,請輸入正確的日期');
}

這一步主要是為了解決像:

20260230

這種問題。

因為 JavaScript 的 Date 遇到超出範圍的日期時,有可能自動往後進位。

所以我不能只建立 Date,還需要再把建立後的年月日讀出來,跟使用者原本輸入的資料比對。

如果不一樣,就代表原本輸入的日期不存在。

最後再檢查是否為過去的時間:

const now = new Date();

if (eventDate <= now) {
    errors.push('不能建立過去的行程');
}

這裡的 now 代表目前時間。

如果:

eventDate <= now

就代表使用者輸入的是現在或過去的時間,因此不允許建立。

完成所有檢查後,如果有任何錯誤,就一次回覆:

if (errors.length > 0) {
    await interaction.reply(errors.join('\n'));
    return;
}

如果完全沒有錯誤,才繼續顯示整理後的行程資訊:

const formattedDate =
    `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;

const formattedTime =
    `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;

await interaction.reply(
    `行程名稱:${title}\n日期:${formattedDate}\n時間:${formattedTime}`
);

三、今日修改的完整程式碼與測試結果

今天主要修改的是 commands/add.js:

async function handleAddCommand(interaction) {
    const title = interaction.options.getString('title');
    const date = interaction.options.getString('date');
    const time = interaction.options.getString('time');

    const dateRegex = /^\d{8}$/;
    const timeRegex = /^\d{4}$/;

    const errors = [];

    if (!dateRegex.test(date)) {
        errors.push('日期格式錯誤,請輸入 8 位數日期,例如 20260903');
    }

    if (!timeRegex.test(time)) {
        errors.push('時間格式錯誤,請輸入 4 位數時間,例如 1830');
    }

    if (errors.length > 0) {
        await interaction.reply(errors.join('\n'));
        return;
    }

    const year = parseInt(date.slice(0, 4));
    const month = parseInt(date.slice(4, 6));
    const day = parseInt(date.slice(6, 8));

    const hour = parseInt(time.slice(0, 2));
    const minute = parseInt(time.slice(2, 4));

    if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
        errors.push('時間不存在,請輸入正確的時間,例如 1830');
    }

    const eventDate = new Date(
        year,
        month - 1,
        day,
        hour,
        minute
    );

    const isValidDate =
        eventDate.getFullYear() === year &&
        eventDate.getMonth() === month - 1 &&
        eventDate.getDate() === day;

    if (!isValidDate) {
        errors.push('日期不存在,請輸入正確的日期');
    }

    const now = new Date();

    if (eventDate <= now) {
        errors.push('不能建立過去的行程');
    }

    if (errors.length > 0) {
        await interaction.reply(errors.join('\n'));
        return;
    }

    const formattedDate =
        `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;

    const formattedTime =
        `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;

    await interaction.reply(
        `行程名稱:${title}\n日期:${formattedDate}\n時間:${formattedTime}`
    );
}

module.exports = handleAddCommand;

這次我測試了幾種不同情況。

正常資料:

date:20261006
time:1830

可以正常顯示行程資訊。

不存在的日期:

date:20260230
time:1830

會顯示:

日期不存在,請輸入正確的日期

錯誤的時間:

date:20261006
time:2960

會顯示:

時間不存在,請輸入正確的時間,例如 1830

如果輸入已經過去的日期時間,也會顯示:

不能建立過去的行程

今天最大的收穫是理解到「格式正確」和「資料合理」是兩件不同的事情。

Regex 只能檢查輸入的外觀是不是符合規則,但如果要真的拿來建立行程,需要進一步確認日期存在、時間是否合理,而且時間點是在未來。


*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言